﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="OrientedRectangleF.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2009, 2010 
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
// <license>
//   This file is part of starLiGHT.Core.
//
//   starLiGHT.Core is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Core is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Core. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Core are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 620                   $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2010-12-07 15:23:58 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Text;
    using Microsoft.Xna.Framework;

    #endregion

    public struct OrientedRectangleF : IEquatable<OrientedRectangleF>
    {
        #region Fields
        private Vector2 topLeft;
        private Vector2 topRight; 
        private bool topRightDirty;
        private Vector2 bottomLeft; 
        private bool bottomLeftDirty;
        private Vector2 bottomRight;
        private Vector2 size; 
        private bool sizeDirty;
        private Vector2 center; 
        private bool centerDirty;
        private Vector2 origin;
        private float rotation;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the OrientedRectangleF struct.
        /// </summary>
        /// <param name="x">The x-coordinate of the unrotated rectangle.</param>
        /// <param name="y">The y-coordinate of the unrotated rectangle.</param>
        /// <param name="width">Width of the rectangle.</param>
        /// <param name="height">Height of the rectangle.</param>
        /// <param name="initialRotation">Rotation of the OrientedRectangleF in radians.</param>
        public OrientedRectangleF(float x, float y, float width, float height, float initialRotation)
        {
            this.rotation = initialRotation;
            this.origin = new Vector2(width * 0.5f, height * 0.5f);

            this.centerDirty = false; 
            this.center = new Vector2(x, y) + this.origin;
            this.sizeDirty   = false; 
            this.size = new Vector2(width, height);

            this.topLeft = new Vector2();
            Vector2 point = new Vector2(x, y);
            this.topLeft.X = (float)(this.origin.X + ((point.X - this.origin.X) * Math.Cos(this.rotation)) - ((point.Y - this.origin.Y) * Math.Sin(this.rotation)));
            this.topLeft.Y = (float)(this.origin.Y + ((point.Y - this.origin.Y) * Math.Cos(this.rotation)) + ((point.X - this.origin.X) * Math.Sin(this.rotation)));

            this.topRightDirty    = false;  
            this.topRight = new Vector2(this.topLeft.X + width, this.topLeft.Y);
            this.bottomLeftDirty  = false;  
            this.bottomLeft = new Vector2(this.topLeft.X, this.topLeft.Y + height);
            this.bottomRight = new Vector2(this.topLeft.X + width, this.topLeft.Y + height);
        }

        public OrientedRectangleF(float x, float y, float width, float height)
        {
            this.rotation = 0.0f;

            this.topLeft = new Vector2(x, y);
            this.topRight = new Vector2(x + width, y);
            this.bottomRight = new Vector2(x + width, y + height);
            this.bottomLeft = new Vector2(x, y + height);

            this.topRightDirty = false;
            this.bottomLeftDirty = false;

            this.size = new Vector2(width, height);
            this.sizeDirty = false;

            this.origin = this.topLeft + (this.size * 0.5f);

            this.center = this.origin;
            this.centerDirty = false;
        }

        /// <summary>
        /// Initializes a new instance of the OrientedRectangleF struct.
        /// </summary>
        /// <param name="topLeft">Vector2 containing the x- and y-coordinate of the rectangle.</param>
        /// <param name="size">Vector2 containing the size (width and height) of the rectangle.</param>
        /// <param name="initialRotation">The initial rotation in radians of the OrientedRectangleF.</param>
        public OrientedRectangleF(Vector2 topLeft, Vector2 size, float initialRotation)
            : this(topLeft.X, topLeft.Y, size.X, size.Y, initialRotation)
        {
        }

        public OrientedRectangleF(Vector2 topLeft, Vector2 size)
            : this(topLeft.X, topLeft.Y, size.X, size.Y, 0.0f)
        {
        }

        /// <summary>
        /// Initializes a new instance of the OrientedRectangleF struct.
        /// </summary>
        /// <param name="topLeft">Vector2 containing the x- and y-coordinate of the rectangle.</param>
        /// <param name="width">Width of the rectangle.</param>
        /// <param name="height">Height of the rectangle.</param>
        /// <param name="initialRotation">The initial rotation in radians of the OrientedRectangleF.</param>
        public OrientedRectangleF(Vector2 topLeft, float width, float height, float initialRotation)
            : this(topLeft.X, topLeft.Y, width, height, initialRotation)
        {
        }

        public OrientedRectangleF(Vector2 topLeft, float width, float height)
            : this(topLeft.X, topLeft.Y, width, height, 0.0f)
        {
        }

        /// <summary>
        /// Initializes a new instance of the OrientedRectangleF struct.
        /// </summary>
        /// <param name="rect">The rectangle to construct the OrientedRectangleF of</param>
        /// <param name="initialRotation">The initial rotation in radians of the OrientedRectangleF.</param>
        public OrientedRectangleF(Rectangle rect, float initialRotation)
            : this(rect.X, rect.Y, rect.Width, rect.Height, initialRotation)
        {
        }

        public OrientedRectangleF(Rectangle rect)
            : this(rect.X, rect.Y, rect.Width, rect.Height, 0.0f)
        {
        }

        public OrientedRectangleF(RectangleF rect, float initialRotation)
            : this(rect.X, rect.Y, rect.Width, rect.Height, initialRotation)
        {
        }

        public OrientedRectangleF(RectangleF rect)
        {
            this.rotation = 0.0f;

            this.topLeft = rect.TopLeft;
            this.topRight = rect.TopRight;
            this.bottomRight = rect.BottomRight;
            this.bottomLeft = rect.BottomLeft;

            this.topRightDirty = false;
            this.bottomLeftDirty = false;

            this.size = rect.Size;
            this.sizeDirty = false;

            this.origin = this.topLeft + (this.size * 0.5f);

            this.center = this.origin;
            this.centerDirty = false;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a RectangleF with all of its values set to zero.
        /// </summary>
        public static OrientedRectangleF Empty
        {
            get { return new OrientedRectangleF(); }
        }

        /// <summary>
        /// Gets the y-coordinate of the bottom of the rectangle.
        /// </summary>
        public float Bottom
        {
            get 
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// Gets a value indicating whether the RectangleF is empty.
        /// </summary>
        public bool IsEmpty
        {
            get { return this.center.X == 0.0f && this.center.Y == 0.0f; }
        }

        /// <summary>
        /// Gets the x-coordinate of the left side of the rectangle.
        /// </summary>
        public float Left
        {
            get
            {
                float ret = Math.Min(this.topLeft.X, this.topRight.X);
                ret = Math.Min(ret, this.bottomRight.X);
                ret = Math.Min(ret, this.bottomLeft.X);

                return ret;
            }
        }

        public float X
        {
            get 
            { 
                float ret = Math.Min(this.topLeft.X, this.topRight.X);
                      ret = Math.Min(ret, this.bottomRight.X);
                      ret = Math.Min(ret, this.bottomLeft.X);

                return ret;
            }
        }

        public float Y
        {
            get
            {
                float ret = Math.Min(this.topLeft.Y, this.topRight.Y);
                ret = Math.Min(ret, this.bottomRight.Y);
                ret = Math.Min(ret, this.bottomLeft.Y);

                return ret;
            }
        }

        public float Width
        {
            get
            {
                if (this.sizeDirty)
                {
                    this.updateSize();
                }

                return this.size.X;
            }

            set
            {
                if (this.size.X != value)
                {
                    this.size.X = value;
                    this.topRightDirty = true;
                    this.bottomLeftDirty = true;
                }
            }
        }

        public float Height
        {
            get
            {
                if (this.sizeDirty)
                {
                    this.updateSize();
                }

                return this.size.Y;
            }

            set
            {
                if (this.size.Y != value)
                {
                    this.size.Y = value;
                    this.topRightDirty = true;
                    this.bottomLeftDirty = true;
                }
            }
        }

        /// <summary>
        /// Gets or sets the upper-left value of the RectangleF.
        /// </summary>
        public Vector2 Location
        {
            get { return this.topLeft; }
            set { this.topLeft = value; }
        }

        public Vector2 TopLeft
        {
            get 
            { 
                return this.topLeft; 
            }

            set
            {
                if (this.topLeft != value)
                {
                    this.topLeft = value;
                    this.bottomLeftDirty = true;
                    this.topRightDirty = true;
                    this.sizeDirty = true;
                    this.centerDirty = true;
                }
            }
        }

        public Vector2 TopRight
        {
            get
            {
                if (this.topRightDirty)
                {
                    this.updateTopRight();
                }

                return this.topRight;
            }
        }

        public Vector2 BottomLeft
        {
            get
            {
                if (this.bottomLeftDirty)
                {
                    this.updateBottomLeft();
                }

                return this.bottomLeft;
            }
        }

        public Vector2 BottomRight
        {
            get 
            { 
                return this.bottomRight; 
            }

            set
            {
                if (this.bottomRight != value)
                {
                    this.bottomRight = value;
                    this.topRightDirty = true;
                    this.bottomLeftDirty = true;
                    this.sizeDirty = true;
                    this.centerDirty = true;
                }
            }
        }

        public Vector2 Size
        {
            get
            {
                if (this.sizeDirty)
                {
                    this.updateSize();
                }

                return this.size;
            }

            set
            {
                if (this.Size != value)
                {
                    this.BottomRight = this.topLeft + value;
                }
            }
        }

        /// <summary>
        /// Gets the Vector2 that specifies the center of the rectangle.
        /// </summary>
        public Vector2 Center
        {
            get
            {
                if (this.centerDirty)
                {
                    this.updateCenter();
                }

                return this.center;
            }
        }

        /// <summary>
        /// Gets the x-coordinate of the right side of the rectangle.
        /// </summary>
        public float Right
        {
            get 
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// Gets the y-coordinate of the top of the rectangle.
        /// </summary>
        public float Top
        {
            get 
            {
                throw new NotImplementedException();
            }
        }

        public float Rotation
        {
            get 
            { 
                return this.rotation; 
            }

            set
            {
                if (this.rotation != value)
                {
                    float diff = value - this.rotation;
                    this.rotation = value;

                    if (diff == 0.0f)
                    {
                        return;
                    }

                    this.topLeft = this.RotatePoint(this.topLeft, this.origin, diff);
                    this.topRight = this.RotatePoint(this.topRight, this.origin, diff);
                    this.bottomRight = this.RotatePoint(this.bottomRight, this.origin, diff);
                    this.bottomLeft = this.RotatePoint(this.bottomLeft, this.origin, diff);
                    this.center = this.RotatePoint(this.center, this.origin, diff);
                }
            }
        }

        public Vector2[] Corners
        {
            get
            {
                return new Vector2[] { this.TopLeft, this.TopRight, this.BottomRight, this.BottomLeft };
            }
        }

        public IEnumerable<Vector2> CornerEnumerable
        {
            get
            {
                yield return this.TopLeft;
                yield return this.TopRight;
                yield return this.BottomRight;
                yield return this.BottomLeft;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Creates a OrientedRectangleF defining the area where one rectangle overlaps with another rectangle.
        /// </summary>
        /// <param name="value1">The first OrientedRectangleF to compare.</param>
        /// <param name="value2">The second OrientedRectangleF to compare.</param>
        /// <returns>The area where the two parameters overlap.</returns>
        /// <remarks>If the two rectangles do not overlap, the resulting OrientedRectangleF will be empty.</remarks>
        public static OrientedRectangleF Intersect(OrientedRectangleF value1, OrientedRectangleF value2)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Creates a OrientedRectangleF defining the area where one rectangle overlaps with another rectangle.
        /// </summary>
        /// <param name="value1">The first OrientedRectangleF to compare.</param>
        /// <param name="value2">The second OrientedRectangleF to compare.</param>
        /// <param name="result">The area where the two parameters overlap.</param>
        /// <remarks>If the two rectangles do not overlap, the resulting OrientedRectangleF will be empty.</remarks>
        public static void Intersect(ref OrientedRectangleF value1, ref OrientedRectangleF value2, out OrientedRectangleF result)
        {
            result = Intersect(value1, value2);
        }

        /// <summary>
        /// Compares two rectangles for equality.
        /// </summary>
        /// <param name="first">First rectangle.</param>
        /// <param name="second">Second rectangle.</param>
        /// <returns>true if the rectangles are equal; false otherwise.</returns>
        public static bool operator ==(OrientedRectangleF first, OrientedRectangleF second)
        {
            return first.X == second.X && 
                   first.Y == second.Y && 
                   first.Width == second.Width && 
                   first.Height == second.Height && 
                   first.Rotation == second.Rotation;
        }

        /// <summary>
        /// Compares two rectangles for inequality.
        /// </summary>
        /// <param name="first">first rectangle.</param>
        /// <param name="second">second rectangle.</param>
        /// <returns>true if the rectangles are not equal; false otherwise.</returns>
        public static bool operator !=(OrientedRectangleF first, OrientedRectangleF second)
        {
            return first.X != second.X || 
                   first.Y != second.Y || 
                   first.Width != second.Width || 
                   first.Height != second.Height || 
                   first.Rotation != second.Rotation;
        }

        /// <summary>
        /// Creates a new OrientedRectangleF that exactly contains two other rectangles. 
        /// </summary>
        /// <param name="value1">The first OrientedRectangleF to contain.</param>
        /// <param name="value2">The second OrientedRectangleF to contain.</param>
        /// <returns>The union of the two OrientedRectangleF parameters.</returns>
        public static OrientedRectangleF Union(OrientedRectangleF value1, OrientedRectangleF value2)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Creates a new OrientedRectangleF that exactly contains two other rectangles. 
        /// </summary>
        /// <param name="value1">The first OrientedRectangleF to contain.</param>
        /// <param name="value2">The second OrientedRectangleF to contain.</param>
        /// <param name="result">The union of the two OrientedRectangleF parameters.</param>
        public static void Union(ref OrientedRectangleF value1, ref OrientedRectangleF value2, out OrientedRectangleF result)
        {
            result = Union(value1, value2);
        }

        public static OrientedRectangleF FromPoints(params Vector2[] points)
        {
            float minX = float.MaxValue;
            float minY = float.MaxValue;
            float maxX = float.MinValue;
            float maxY = float.MinValue;

            foreach (Vector2 p in points)
            {
                minX = Math.Min(minX, p.X);
                minY = Math.Min(minY, p.Y);
                maxX = Math.Max(maxX, p.X);
                maxY = Math.Max(maxY, p.Y);
            }

            return new OrientedRectangleF(minX, minY, maxX - minX, maxY - minY);
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF contains a specified point represented by its x- and y-coordinates.
        /// </summary>
        /// <param name="x">The x-coordinate of the specified point.</param>
        /// <param name="y">The y-coordinate of the specified point.</param>
        /// <returns>true if the specified point is contained within this OrientedRectangleF; false otherwise.</returns>
        public bool Contains(float x, float y)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF contains a specified Point.
        /// </summary>
        /// <param name="value">The Point to evaluate.</param>
        /// <returns>true if the specified Point is contained within this OrientedRectangleF; false otherwise.</returns>
        public bool Contains(Point value)
        {
            return this.Contains(value.X, value.Y);
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF contains a specified Vector2.
        /// </summary>
        /// <param name="value">The Vector2 to evaluate.</param>
        /// <returns>true if the specified Vector2 is contained within this OrientedRectangleF; false otherwise.</returns>
        public bool Contains(Vector2 value)
        {
            return this.Contains(value.X, value.Y);
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF contains a specified Point.
        /// </summary>
        /// <param name="value">The Point to evaluate.</param>
        /// <param name="result">true if the specified Point is contained within this OrientedRectangleF; false otherwise.</param>
        public void Contains(ref Point value, out bool result)
        {
            result = this.Contains(value.X, value.Y);
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF contains a specified Vector2.
        /// </summary>
        /// <param name="value">The Vector2 to evaluate.</param>
        /// <param name="result">true if the specified Vector2 is contained within this OrientedRectangleF; false otherwise.</param>
        public void Contains(ref Vector2 value, out bool result)
        {
            result = this.Contains(value.X, value.Y);
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF entirely contains a specified OrientedRectangleF.
        /// </summary>
        /// <param name="value">The OrientedRectangleF to evaluate. </param>
        /// <returns>true if this OrientedRectangleF entirely contains the specified OrientedRectangleF; false otherwise.</returns>
        public bool Contains(OrientedRectangleF value)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF entirely contains a specified RectangleF.
        /// </summary>
        /// <param name="value">The RectangleF to evaluate. </param>
        /// <returns>true if this OrientedRectangleF entirely contains the specified RectangleF; false otherwise.</returns>
        public bool Contains(RectangleF value)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF entirely contains a specified Rectangle.
        /// </summary>
        /// <param name="value">The Rectangle to evaluate.</param>
        /// <returns>true if this OrientedRectangleF entirely contains the specified Rectangle; false otherwise.</returns>
        public bool Contains(Rectangle value)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF entirely contains a specified RectangleF.
        /// </summary>
        /// <param name="value">The RectangleF to evaluate.</param>
        /// <param name="result">On exit, is true if this OrientedRectangleF entirely contains the specified RectangleF, or false if not.</param>
        public void Contains(ref RectangleF value, out bool result)
        {
            result = this.Contains(value);
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF entirely contains a specified OrientedRectangleF.
        /// </summary>
        /// <param name="value">The OrientedRectangleF to evaluate.</param>
        /// <param name="result">On exit, is true if this OrientedRectangleF entirely contains the specified OrientedRectangleF, or false if not.</param>
        public void Contains(ref OrientedRectangleF value, out bool result)
        {
            result = this.Contains(value);
        }

        /// <summary>
        /// Determines whether this OrientedRectangleF entirely contains a specified Rectangle.
        /// </summary>
        /// <param name="value">The Rectangle to evaluate.</param>
        /// <param name="result">On exit, is true if this OrientedRectangleF entirely contains the specified Rectangle, or false if not.</param>
        public void Contains(ref Rectangle value, out bool result)
        {
            result = this.Contains(value);
        }

        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a specified OrientedRectangleF.
        /// </summary>
        /// <param name="other">The OrientedRectangleF to compare with the current OrientedRectangleF.</param>
        /// <returns>true if the specified OrientedRectangleF is equal to the current OrientedRectangleF; false otherwise.</returns>
        public bool Equals(OrientedRectangleF other)
        {
            return this.X == other.X && 
                   this.Y == other.Y && 
                   this.Width == other.Width && 
                   this.Height == other.Height && 
                   this.Rotation == other.Rotation;
        }

        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a specified object.
        /// </summary>
        /// <param name="obj">The Object to compare with the current OrientedRectangleF.</param>
        /// <returns>true if the specified object is equal to the current OrientedRectangleF; false otherwise.</returns>
        public override bool Equals(object obj)
        {
            if (obj is OrientedRectangleF)
            {
                return this.Equals((OrientedRectangleF)obj);
            }

            return false;
        }

        /// <summary>
        /// Gets the hash code for this object.
        /// </summary>
        /// <returns>Hash code for this object.</returns>
        public override int GetHashCode()
        {
            return this.X.GetHashCode() + 
                   this.Y.GetHashCode() + 
                   this.Width.GetHashCode() + 
                   this.Height.GetHashCode() + 
                   this.Rotation.GetHashCode();
        }

        /// <summary>
        /// Pushes the edges of the OrientedRectangleF out by the horizontal and vertical values specified.
        /// </summary>
        /// <param name="horizontalAmount">Value to push the sides out by.</param>
        /// <param name="verticalAmount">Value to push the top and bottom out by.</param>
        /// <remarks>Each corner of the OrientedRectangleF is pushed away from the center of the 
        /// rectangle by the specified amounts. This results in the width and height of 
        /// the OrientedRectangleF increasing by twice the values provided.</remarks>
        public void Inflate(float horizontalAmount, float verticalAmount)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Determines whether a specified OrientedRectangleF intersects with this OrientedRectangleF. 
        /// </summary>
        /// <param name="value">The OrientedRectangleF to evaluate.</param>
        /// <returns>true if the specified OrientedRectangleF intersects with this one; false otherwise.</returns>
        public bool Intersects(OrientedRectangleF value)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Determines whether a specified OrientedRectangleF intersects with this OrientedRectangleF. 
        /// </summary>
        /// <param name="value">The OrientedRectangleF to evaluate.</param>
        /// <param name="result">true if the specified OrientedRectangleF intersects with this one; false otherwise.</param>
        public void Intersects(ref OrientedRectangleF value, out bool result)
        {
            result = this.Intersects(value);
        }

        /// <summary>
        /// Changes the position of the OrientedRectangleF.
        /// </summary>
        /// <param name="offsetX">Change in the x-position.</param>
        /// <param name="offsetY">Change in the y-position.</param>
        public void Offset(float offsetX, float offsetY)
        {
            this.center.X += offsetX;
            this.center.Y += offsetY;
        }

        /// <summary>
        /// Changes the position of the OrientedRectangleF.
        /// </summary>
        /// <param name="offsetX">Change in the x-position.</param>
        /// <param name="offsetY">Change in the y-position.</param>
        public void Offset(int offsetX, int offsetY)
        {
            this.center.X += offsetX;
            this.center.Y += offsetY;
        }

        /// <summary>
        /// Changes the position of the OrientedRectangleF.
        /// </summary>
        /// <param name="amount">The values to adjust the position of the OrientedRectangleF by.</param>
        public void Offset(Vector2 amount)
        {
            this.topLeft += amount;
            this.topRight += amount;
            this.bottomLeft += amount;
            this.bottomRight += amount;
            this.center += amount;
        }

        /// <summary>
        /// Changes the position of the OrientedRectangleF.
        /// </summary>
        /// <param name="amount">The values to adjust the position of the OrientedRectangleF by.</param>
        public void Offset(Point amount)
        {
            this.center.X += amount.X;
            this.center.Y += amount.Y;
        }

        /// <summary>
        /// Retrieves a string representation of the current object.
        /// </summary>
        /// <returns>string that represents the object.</returns>
        public override string ToString()
        {
            CultureInfo currentCulture = CultureInfo.CurrentCulture;

            return string.Format(
                                 currentCulture, 
                                 "{{X:{0} Y:{1} Width:{2} Height:{3} Rotation:{4}}}",
                                 this.X.ToString(currentCulture),
                                 this.Y.ToString(currentCulture),
                                 this.Width.ToString(currentCulture),
                                 this.Height.ToString(currentCulture),
                                 this.Rotation.ToString(currentCulture));
        }

        public void Scale(float scale)
        {
            this.Scale(new Vector2(scale, scale));
        }

        public void Scale(Vector2 scale)
        {
            float oldWidth = this.Width;
            float width = this.Width * scale.X;
            float halfWidth = this.Width * 0.5f;

            float oldHeight = this.Height;
            float height = this.Height * scale.Y;
            float halfHeight = this.Height * 0.5f;

            this.center.X -= (height - oldHeight) * 0.5f;
            this.center.Y -= (width - oldWidth) * 0.5f;
            this.Width = width;
            this.Height = height;
        }

        public void Rotate(float radians)
        {
            if (radians == 0.0f)
            {
                return;
            }

            this.topLeft = this.RotatePoint(this.topLeft, this.origin, radians);
            this.topRight = this.RotatePoint(this.topRight, this.origin, radians);
            this.bottomRight = this.RotatePoint(this.bottomRight, this.origin, radians);
            this.bottomLeft = this.RotatePoint(this.bottomLeft, this.origin, radians);
            this.center = this.RotatePoint(this.center, this.origin, radians);
        }

        public void Rotate(float radians, Vector2 origin)
        {
            if (radians == 0.0f)
            {
                return;
            }

            this.topLeft = this.RotatePoint(this.topLeft, this.origin, radians);
            this.topRight = this.RotatePoint(this.topRight, this.origin, radians);
            this.bottomRight = this.RotatePoint(this.bottomRight, this.origin, radians);
            this.bottomLeft = this.RotatePoint(this.bottomLeft, this.origin, radians);
            this.center = this.RotatePoint(this.center, this.origin, radians);
        }

        public OrientedRectangleF Round()
        {
            return new OrientedRectangleF(this.Location.Round(), this.Size.Round());
        }

        public Rectangle ToRectangle()
        {
            float minX = float.MaxValue;
            float minY = float.MaxValue;
            float maxX = float.MinValue;
            float maxY = float.MinValue;

            foreach (Vector2 p in this.CornerEnumerable)
            {
                minX = Math.Min(minX, p.X);
                minY = Math.Min(minY, p.Y);
                maxX = Math.Max(maxX, p.X);
                maxY = Math.Max(maxY, p.Y);
            }

            return new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
        }

        public RectangleF ToRectangleF()
        {
            //// // TODO: seems like RectangleF moves to Vector2.Zero with TopLeft after converting :-(

            float minX = float.MaxValue;
            float minY = float.MaxValue;
            float maxX = float.MinValue;
            float maxY = float.MinValue;

            foreach (Vector2 p in this.CornerEnumerable)
            {
                minX = Math.Min(minX, p.X);
                minY = Math.Min(minY, p.Y);
                maxX = Math.Max(maxX, p.X);
                maxY = Math.Max(maxY, p.Y);
            }

            return new RectangleF(minX, minY, maxX - minX, maxY - minY);
        }

        #endregion

        #region Helpers
        private void updateSize()
        {
            this.size.X = this.BottomRight.X - this.TopLeft.X;
            this.size.Y = this.BottomRight.Y - this.TopLeft.Y;
            this.sizeDirty = false;
        }

        private void updateBottomLeft()
        {
            this.bottomLeft.X = this.topLeft.X;
            this.bottomLeft.Y = this.bottomRight.Y;
            this.bottomLeftDirty = false;
        }

        private void updateTopRight()
        {
            this.topRight.X = this.BottomRight.X;
            this.topRight.Y = this.TopLeft.Y;
            this.topRightDirty = false;
        }

        private void updateCenter()
        {
            this.center.X = this.TopLeft.X + ((this.BottomRight.X - this.TopLeft.X) * 0.5f);
            this.center.Y = this.TopLeft.Y + ((this.BottomRight.Y - this.TopLeft.Y) * 0.5f);
            this.centerDirty = false;
        }

        private Vector2 RotatePoint(Vector2 point, Vector2 origin, float rotation)
        {
            Vector2 translatedPoint = new Vector2();
            translatedPoint.X = (float)(origin.X + ((point.X - origin.X) * Math.Cos(rotation)) - ((point.Y - origin.Y) * Math.Sin(rotation)));
            translatedPoint.Y = (float)(origin.Y + ((point.Y - origin.Y) * Math.Cos(rotation)) + ((point.X - origin.X) * Math.Sin(rotation)));
            return translatedPoint;
        }

        #endregion
    }
}
